﻿// -----------------------------------------------------------------------
// <copyright file="HourlyCalendar.cs" company="The Usual Dosage">
// © 2013 by Matthew M. Martin
// </copyright>
// -----------------------------------------------------------------------

namespace Cadence.NET.Calendars
{
    using System;
    using System.Collections.Generic;
    using Cadence.NET.BaseClasses;

    /// <summary>
    /// Describes the methods and properties of a hour-based calendar.
    /// </summary>
    public class HourlyCalendar : ICalendar
    {
        #region Properties

        /// <summary>
        /// Tasks will only be performed between StartTime and EndTime.
        /// </summary>
        /// <value>
        /// The start hour.
        /// </value>
        private Time startTime;
        public Time StartTime
        {
            get
            {
                return startTime;
            }
            set
            {
                if (EndTime != null && (EndTime.Hour.HasValue) && (value.Hour > EndTime.Hour))
                    throw new InvalidOperationException("StartTime must be before EndTime.");
                else
                    startTime = value;
            }
        }

        /// <summary>
        /// Tasks will only be performed between StartTime and EndTime.
        /// </summary>
        /// <value>
        /// The end hour.
        /// </value>
        private Time endTime;
        public Time EndTime 
        {
            get
            {
                return endTime;
            }
            set
            {
                if (StartTime != null && (StartTime.Hour.HasValue) && (value.Hour < StartTime.Hour))
                    throw new InvalidOperationException("EndTime must be after StartTime.");
                else
                    endTime = value;
            }
        }


        /// <summary>
        /// Gets or sets the exclude times.
        /// </summary>
        /// <value>
        /// The exclude times.
        /// </value>
        public List<Time> ExcludeTimes { get; set; }

        /// <summary>
        /// Gets or sets the override times.
        /// </summary>
        /// <value>
        /// The override times.
        /// </value>
        public List<Time> OverrideTimes { get; set; }

        /// <summary>
        /// Gets or sets the timezone offset from UTC.
        /// </summary>
        /// <value>
        /// The timezone offset.
        /// </value>
        public int? TimezoneUTCOffset
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the current date time.
        /// </summary>
        public DateTime CurrentDateTime
        {
            get
            {
                if (TimezoneUTCOffset.HasValue)
                    return DateTime.UtcNow.AddHours(TimezoneUTCOffset.Value);
                else
                    return DateTime.UtcNow;
            }
        }

        #endregion Properties

        #region Interface Implementation

        /// <summary>
        /// When implemented in a derived class, checks the time configured, and indicates whether this instance can run.
        /// </summary>
        /// <returns>
        ///   <c>true</c> if this instance can run; otherwise, <c>false</c>.
        /// </returns>
        public bool CanRun()
        {
            if (StartTime == null || EndTime == null)
            {
                throw new ArgumentNullException("Hourly calendars must have a start and end time.");
            }

            if (!EndTime.IsGreaterThanEqualTo(StartTime))
            {
                throw new ArgumentOutOfRangeException("StartTime must come before EndTime.");
            }

            Time currentTime = CurrentDateTime.ToTime();

            if (OverrideTimes != null && OverrideTimes.Contains(currentTime))
                return true;
            else if (ExcludeTimes != null && ExcludeTimes.Contains(currentTime))
                return false;
            else if (currentTime.IsGreaterThanEqualTo(StartTime) && !currentTime.IsGreaterThanEqualTo(EndTime))
                return true;
            else
                return false;
        }

        #endregion Interface Implementation
    }
}
